<?php if(!defined("BASEPATH")){ exit("No direct script access allowed"); }

    /**
     * Multi-Upload
     *
     * Extends CodeIgniters native Upload class to add support for multiple
     * uploads.
     *
     * @package        CodeIgniter
     * @subpackage    Libraries
     * @category    Uploads
     * @author        Conveyor Group <steven@conveyorgroup.com>
     * @link        https://github.com/stvnthomas/CodeIgniter-2.1-Multi-Upload
     */
        class MY_Upload extends CI_Upload {


            /**
             * Properties
             */
                 protected $_multi_upload_data            = array();
                protected $_multi_file_name_override    = "";


            /**
             * Initialize preferences
             *
             * @access    public
             * @param    array
             * @return    void
             */
                public function initialize($config = array()){
                    //Upload default settings.
                    $defaults = array(
                                    "max_size"            => 0,
                                    "max_width"            => 0,
                                    "max_height"        => 0,
                                    "max_filename"        => 0,
                                    "allowed_types"        => "",
                                    "file_temp"            => "",
                                    "file_name"            => "",
                                    "orig_name"            => "",
                                    "file_type"            => "",
                                    "file_size"            => "",
                                    "file_ext"            => "",
                                    "upload_path"        => "",
                                    "overwrite"            => FALSE,
                                    "encrypt_name"        => FALSE,
                                    "is_image"            => FALSE,
                                    "image_width"        => "",
                                    "image_height"        => "",
                                    "image_type"        => "",
                                    "image_size_str"    => "",
                                    "error_msg"            => array(),
                                    "mimes"                => array(),
                                    "remove_spaces"        => TRUE,
                                    "xss_clean"            => FALSE,
                                    "temp_prefix"        => "temp_file_",
                                    "client_name"        => ""
                                );

                    //Set each configuration.
                    foreach($defaults as $key => $val){
                        if(isset($config[$key])){
                            $method = "set_{$key}";
                            if(method_exists($this, $method)){
                                $this->$method($config[$key]);
                            } else {
                                $this->$key = $config[$key];
                            }
                        } else {
                            $this->$key = $val;
                        }
                    }

                    //Check if file_name was provided.
                    if(!empty($this->file_name)){
                        //Multiple file upload.
                        if(is_array($this->file_name)){
                            //Clear file name override.
                            $this->_file_name_override = "";

                            //Set multiple file name override.
                            $this->_multi_file_name_override = $this->file_name;
                        //Single file upload.
                        } else {
                            //Set file name override.
                            $this->_file_name_override = $this->file_name;

                            //Clear multiple file name override.
                            $this->_multi_file_name_override = "";
                        }
                    }
                }


            /**
             * File MIME Type
             *
             * Detects the (actual) MIME type of the uploaded file, if possible.
             * The input array is expected to be $_FILES[$field].
             *
             * In the case of multiple uploads, a optional second argument may be
             * passed specifying which array element of the $_FILES[$field] array
             * elements should be referenced (name, type, tmp_name, etc).
             *
             * @access    protected
             * @param    $file    array
             * @param    $count    int
             * @return    void
             */
                protected function _file_mime_type($file, $count=0){
                    //Mutliple file?
                    if(is_array($file["name"])){
                        $tmp_name = $file["tmp_name"][$count];
                        $type = $file["type"][$count];
                    //Single file.
                    } else {
                        $tmp_name = $file["tmp_name"];
                        $type = $file["type"];
                    }

                    //We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii).
                    $regexp = "/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/";

                    /* Fileinfo Extension - most reliable method.
                     *
                     * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
                     * more convenient FILEINFO_MIME_TYPE flag doesn't exist.
                     */
                         if(function_exists("finfo_file")){
                             $finfo = finfo_open(FILEINFO_MIME);
                            if(is_resource($finfo)){
                                $mime = @finfo_file($finfo, $tmp_name);
                                finfo_close($finfo);

                                /* According to the comments section of the PHP manual page,
                                 * it is possible that this function returns an empty string
                                 * for some files (e.g. if they don't exist in the magic MIME database).
                                 */
                                     if(is_string($mime) && preg_match($regexp, $mime, $matches)){
                                         $this->file_type = $matches[1];
                                        return;
                                     }
                            }
                         }

                    /* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
                     * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
                     * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
                     * than mime_content_type() as well, hence the attempts to try calling the command line with
                     * three different functions.
                     *
                     * Notes:
                     *    - the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
                     *    - many system admins would disable the exec(), shell_exec(), popen() and similar functions
                     *      due to security concerns, hence the function_exists() checks
                     */
                         if(DIRECTORY_SEPARATOR !== "\\"){
                             $cmd = "file --brief --mime ".escapeshellarg($tmp_name)." 2>&1";

                            if(function_exists("exec")){
                                /* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
                                 * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
                                 * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
                                 * value, which is only put to allow us to get the return status code.
                                 */
                                    $mime = @exec($cmd, $mime, $return_status);
                                    if($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches)){
                                        $this->file_type = $matches[1];
                                        return;
                                    }
                            }
                         }

                        if((bool)@ini_get("safe_mode") === FALSE && function_exists("shell_exec")){
                            $mime = @shell_exec($cmd);
                            if(strlen($mime) > 0){
                                $mime = explode("\n", trim($mime));
                                if(preg_match($regexp, $mime[(count($mime) - 1)], $matches)){
                                    $this->file_type = $matches[1];
                                    return;
                                }
                            }
                        }

                        if(function_exists("popen")){
                            $proc = @popen($cmd, "r");
                            if(is_resource($proc)){
                                $mime = @fread($proc, 512);
                                @pclose($proc);
                                if($mime !== FALSE){
                                    $mime = explode("\n", trim($mime));
                                    if(preg_match($regexp, $mime[(count($mime) - 1)], $matches)){
                                        $this->file_type = $matches[1];
                                        return;
                                    }
                                }
                            }
                        }

                        //Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]["type"])
                        if(function_exists("mime_content_type")){
                            $this->file_type = @mime_content_type($tmp_name);
                            //It's possible that mime_content_type() returns FALSE or an empty string.
                            if(strlen($this->file_type) > 0){
                                return;
                            }
                        }

                        //If all else fails, use $_FILES default mime type.
                        $this->file_type = $type;
                }


            /**
             * Set Multiple Upload Data
             *
             * @access    protected
             * @return    void
             */
                protected function set_multi_upload_data(){
                    $this->_multi_upload_data[] = array(
                        "file_name"            => str_replace(array('ş','Ş','ı','İ','ğ','Ğ','ü','Ü','ç','Ç','ö','Ö'),array('s','S','i','I','g','G','u','U','c','C','o','O'),$this->file_name),
                        "file_type"            => $this->file_type,
                        "file_path"            => $this->upload_path,
                        "full_path"            => $this->upload_path.$this->file_name,
                        "raw_name"            => str_replace($this->file_ext, "", $this->file_name),
                        "orig_name"            => $this->orig_name,
                        "client_name"        => $this->client_name,
                        "file_ext"            => $this->file_ext,
                        "file_size"            => $this->file_size,
                        "is_image"            => $this->is_image(),
                        "image_width"        => $this->image_width,
                        "image_height"        => $this->image_height,
                        "image_type"        => $this->image_type,
                        "image_size_str"    => $this->image_size_str
                    );
                }


            /**
             * Get Multiple Upload Data
             *
             * @access    public
             * @return    array
             */
                public function get_multi_upload_data(){
                    return $this->_multi_upload_data;
                }


            /**
             * Multile File Upload
             *
             * @access    public
             * @param    string
             * @return    mixed
             */
                public function do_multi_upload($field){
                    //Is $_FILES[$field] set? If not, no reason to continue.
                    if(!isset($_FILES[$field])){ return false; }

                    //Is this really a multi upload?
                    if(!is_array($_FILES[$field]["name"])){
                        //Fallback to do_upload method.
                        return $this->do_upload($field);
                    }

                    //Is the upload path valid?
                    if(!$this->validate_upload_path()){
                        //Errors will already be set by validate_upload_path() so just return FALSE
                        return FALSE;
                    }

                    //Every file will have a separate entry in each of the $_FILES associative array elements (name, type, etc).
                    //Loop through $_FILES[$field]["name"] as representative of total number of files. Use count as key in
                    //corresponding elements of the $_FILES[$field] elements.
                    for($i=0; $i<count($_FILES[$field]["name"]); $i++){
                        //Was the file able to be uploaded? If not, determine the reason why.
                        if(!is_uploaded_file($_FILES[$field]["tmp_name"][$i])){
                            //Determine error number.
                            $error = (!isset($_FILES[$field]["error"][$i])) ? 4 : $_FILES[$field]["error"][$i];

                            //Set error.
                            switch($error){
                                //UPLOAD_ERR_INI_SIZE
                                case 1:
                                    $this->set_error("upload_file_exceeds_limit");
                                break;
                                //UPLOAD_ERR_FORM_SIZE
                                case 2:
                                    $this->set_error("upload_file_exceeds_form_limit");
                                break;
                                //UPLOAD_ERR_PARTIAL
                                case 3:
                                    $this->set_error("upload_file_partial");
                                break;
                                //UPLOAD_ERR_NO_FILE
                                case 4:
                                    $this->set_error("upload_no_file_selected");
                                break;
                                //UPLOAD_ERR_NO_TMP_DIR
                                case 6:
                                    $this->set_error("upload_no_temp_directory");
                                break;
                                //UPLOAD_ERR_CANT_WRITE
                                case 7:
                                    $this->set_error("upload_unable_to_write_file");
                                break;
                                //UPLOAD_ERR_EXTENSION
                                case 8:
                                    $this->set_error("upload_stopped_by_extension");
                                break;
                                default:
                                    $this->set_error("upload_no_file_selected");
                                break;
                            }

                            //Return failed upload.
                            return FALSE;
                        }

                        //Set current file data as class variables.
                        $this->file_temp    = $_FILES[$field]["tmp_name"][$i];
                        $this->file_size    = $_FILES[$field]["size"][$i];
                        $this->_file_mime_type($_FILES[$field], $i);
                        $this->file_type    = preg_replace("/^(.+?);.*$/", "\\1", $this->file_type);
                        $this->file_type    = strtolower(trim(stripslashes($this->file_type), '"'));
                        $this->file_name    = $this->_prep_filename($_FILES[$field]["name"][$i]);
                        $this->file_ext        = $this->get_extension($this->file_name);
                        $this->client_name    = $this->file_name;

                        //Is the file type allowed to be uploaded?
                        if(!$this->is_allowed_filetype()){
                            $this->set_error("upload_invalid_filetype");
                            return FALSE;
                        }

                        //If we're overriding, let's now make sure the new name and type is allowed.
                        //Check if a filename was supplied for the current file. Otherwise, use it's given name.
                        if(!empty($this->_multi_file_name_override[$i])){
                            $this->file_name = $this->_prep_filename($this->_multi_file_name_override[$i]);

                            //If no extension was provided in the file_name config item, use the uploaded one.
                            if(strpos($this->_multi_file_name_override[$i], ".") === FALSE){
                                $this->file_name .= $this->file_ext;
                            //An extension was provided, lets have it!
                            } else {
                                $this->file_ext = $this->get_extension($this->_multi_file_name_override[$i]);
                            }

                            if(!$this->is_allowed_filetype(TRUE)){
                                $this->set_error("upload_invalid_filetype");
                                return FALSE;
                            }
                        }

                        //Convert the file size to kilobytes.
                        if($this->file_size > 0){
                            $this->file_size = round($this->file_size/1024, 2);
                        }

                        //Is the file size within the allowed maximum?
                        if(!$this->is_allowed_filesize()){
                            $this->set_error("upload_invalid_filesize");
                            return FALSE;
                        }

                        //Are the image dimensions within the allowed size?
                        //Note: This can fail if the server has an open_basdir restriction.
                        if(!$this->is_allowed_dimensions()){
                            $this->set_error("upload_invalid_dimensions");
                            return FALSE;
                        }

                        //Sanitize the file name for security.
                        $this->file_name = $this->clean_file_name($this->file_name);

                        //Truncate the file name if it's too long
                        if($this->max_filename > 0){
                            $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
                        }

                        //Remove white spaces in the name
                        if($this->remove_spaces == TRUE){
                            $this->file_name = preg_replace("/\s+/", "_", $this->file_name);
                        }

                        /* Validate the file name
                         * This function appends an number onto the end of
                         * the file if one with the same name already exists.
                         * If it returns false there was a problem.
                         */
                            $this->orig_name = $this->file_name;
                            if($this->overwrite == FALSE){
                                $this->file_name = $this->set_filename($this->upload_path, $this->file_name);
                                if($this->file_name === FALSE){
                                    return FALSE;
                                }
                            }

                        /* Run the file through the XSS hacking filter
                         * This helps prevent malicious code from being
                         * embedded within a file.  Scripts can easily
                         * be disguised as images or other file types.
                         */
                            if($this->xss_clean){
                                if($this->do_xss_clean() === FALSE){
                                    $this->set_error("upload_unable_to_write_file");
                                    return FALSE;
                                }
                            }

                        /* Move the file to the final destination
                         * To deal with different server configurations
                         * we'll attempt to use copy() first.  If that fails
                         * we'll use move_uploaded_file().  One of the two should
                         * reliably work in most environments
                         */
                            if(!@copy($this->file_temp, $this->upload_path.$this->file_name)){
                                if(!@move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name)){
                                    $this->set_error("upload_destination_error");
                                    return FALSE;
                                }
                            }

                        /* Set the finalized image dimensions
                         * This sets the image width/height (assuming the
                         * file was an image).  We use this information
                         * in the "data" function.
                         */
                            $this->set_image_properties($this->upload_path.$this->file_name);

                        //Set current file data to multi_file_upload_data.
                        $this->set_multi_upload_data();
                    }

                    //Return all file upload data.
                    return TRUE;
            }
        }